﻿using PMS.Core.Infrastructure;
using PMS.Services.EntityServices.Logging;
using System;
using System.Linq;

namespace PMS.Services.Utils
{
    // https://gist.github.com/jbogard/54d6569e883f63afebc7
    // http://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/
    public class EventDispatcher : IEventDispatcher
    {
        public EventDispatcher()
        {
        }

        /// <summary>
        /// 要使用本方法，必须赋值完整的消息对象，例：
        /// _dispatcher.Dispatch(new EntityInsertedEvent<ToDoItem>(new ToDoItem()));
        /// 否则无法通过通过Ioc方式(DependencyRegistrar)反射加载对应的处理消息对象并调用消息响应处理方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="domainEvent"></param>
        public void Dispatch<TEvent>(TEvent domainEvent) where TEvent : BaseDomainEvent
        {
            //DavidLee 2019/11/12 更改消息派发机制在接口容器注册时 see DependenceyRegistrar.cs
            //进行所有派发消息处理类的 接口-实现类注册绑定。通过容器获取
            //Type handlerType = typeof(IHandle<>).MakeGenericType(domainEvent.GetType());
            //Type wrapperType = typeof(DomainEventHandler<>).MakeGenericType(domainEvent.GetType());
            //IEnumerable handlers = (IEnumerable)EngineContext.Current.Resolve(typeof(IEnumerable<>).MakeGenericType(handlerType));
            //IEnumerable<DomainEventHandler<T>> wrappedHandlers = handlers.Cast<object>()
            //    .Select(handler => (DomainEventHandler<T>)Activator.CreateInstance(wrapperType, handler));

            //foreach (DomainEventHandler handler in wrappedHandlers)
            //{
            //    handler.Handle(domainEvent);
            //}
            var wrappedHandlers = EngineContext.Current.ResolveAll<IEventHandler<TEvent>>().ToList();
            foreach (IEventHandler<TEvent> handler in wrappedHandlers)
            {
                try
                {
                    //try to handle published event
                    handler.HandleEvent(domainEvent);
                }
                catch (Exception exception)
                {
                    //log error, we put in to nested try-catch to prevent possible cyclic (if some error occurs)
                    try
                    {
                        EngineContext.Current.Resolve<ILogger>()?.Error(exception.Message, exception);
                    }
                    catch
                    {
                        // ignored
                    }
                }
            }
        }
    }
}
